#include "stdafx.h"
#ifdef WIN32
#include <io.h>
#else
#include <unistd.h> 
#endif

#include "TString.h"
#include "SLexer.h"
#include "JsonNode.h"

typedef unsigned long  u_long;
#define CONST_PATH_SEP                    '/' 

/* Architecture of this host */
#define PACKAGE_MACHINE "x86_64"
/* SVN release of this package */
#define PACKAGE_RELEASE "r328"
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.3"

char mProgram[64];

char * gNames[16];

void Usage() {

  printf("%s %s v.%s - (C) 2017-12 atdm.liao\n\n"
	 "Usage:\n"
	 "  %s [-h]|[-f] <node name>  <string>|<file> \n",
	 mProgram, PACKAGE_MACHINE, PACKAGE_VERSION,mProgram);
}

#define GETONE( a )  if( (type=lexer.Scan())<0 ) {  printf("error syntax, %s\n",lexer.error()); delete a; return NULL;}
JsonNode * ReadNode(SLexer & lexer);

BraceNode * ReadQuote(SLexer & lexer,int k)
{
	BraceNode * tmp;
	if( k=='[' ) { k =']'; tmp = new SquareNode(); }
	else { k='}'; tmp = new BraceNode(); }
	int type;
	while(TRUE){
		GETONE( tmp);
		if( type == 0 ) {
			printf("error syntax, unexpected end while look for '%c'\n",k);
			delete tmp;
			return NULL;
		}
		if( (type == SLexer::LX_ID) || (type==SLexer::LX_STRING) ){
			lexer.Push(type);
			if( tmp->Add(ReadNode(lexer))== NULL ){ delete tmp; return NULL; }
			GETONE(tmp);
			if( type != ',' ) lexer.Push(type); 
			continue;
		}
		if( (type=='[') || (type=='{') ){
			if( tmp->Add(ReadQuote(lexer,k))==NULL ) { delete tmp; return NULL;}
			GETONE(tmp);
			if( type != ',' ) lexer.Push(type); 
			continue;
		}
		if( type == k ) break;
		printf("error syntax, expected '%c' at %d\n",k,lexer.pos());
		delete tmp;
		return NULL;
	}
	return tmp;
}
JsonNode * ReadNode(SLexer & lexer){
	int type;
	if( (type=lexer.Scan())<0 ) { 
		printf("error syntax, %s\n",lexer.error()); 
		return NULL;
	}
	if( type==0 ) return NULL;

	if( (type==SLexer::LX_ID) || (type==SLexer::LX_STRING) ) {
		PairNode *tmp = new PairNode(lexer.txt());
		GETONE(tmp);
		if( type!=':') { 
			printf("error syntax, expect ':' at %d\n",lexer.pos()); 
			delete tmp;
			return NULL; 
		}
		GETONE(tmp);
		if( (type==SLexer::LX_ID) || (type==SLexer::LX_STRING) ){
			tmp->SetValue(new TxtNode(lexer.txt()));
			return tmp;
		}
		if( (type=='[')  || (type=='{') ) {
			JsonNode * tmp2=ReadQuote(lexer,type);
			if( tmp2==NULL) { delete tmp; return NULL; }
			tmp->SetValue(tmp2);
			return tmp;
		}
		delete tmp;
		printf(" error syntax, unexpect '%c' at %d\n",type,lexer.pos());
		return NULL;
	}
	if( (type =='{')||(type=='[') )  return ReadQuote(lexer,type);
	
	printf("error synta, expact '{,' or '[' at %d\n",lexer.pos());
	return NULL;

}
JsonNode * FindChildFirst(JsonNode * node,int level)
{
	if( gNames[level]== NULL ) return node;
	JsonNode * rs;
	if( node->GetName() ) {
		if( strcmp(node->GetName(),gNames[level])==0 ) return node;
	}

	for(int i=0;i<node->GetSize();i++) {
		rs = FindChildFirst(node->GetAt(i),level);
		if( rs ) return rs;
	}
	return NULL;
}
JsonNode * FindChildNext(JsonNode * first,int level)
{
	if( gNames[level]== NULL ) return first;

	JsonNode * rs;
	if( first->GetName() ) {
		if( strcmp(first->GetName(),gNames[level]) ) return NULL;
		level ++;
		if( gNames[level] == NULL ) return first;
	}

	for(int i=0;i<first->GetSize();i++) {
		rs = FindChildNext(first->GetAt(i),level);
		if( rs ) return rs;
	}
	return NULL;
}

int GetFileString(const char * name,TString& dest)
{
  FILE * fp = fopen(name,"rb");
  if( fp == NULL )	  return -1;
  
  fseek(fp,0,SEEK_END);
  int length = ftell(fp);
  char * tmp = dest.GetBuffer(length);
  fseek(fp,0,SEEK_SET);
  fread(tmp,1,length,fp);
  fclose(fp);
  dest.ReleaseBuffer();
	return length;
}
void StrCopy(const char * src,char * dest)
{
   int length = strlen(src);
   memset(dest,0,length+1);
   char ch;
   int index=0;
   for(int i=0;i<length;i++){
	   ch = src[i];
	   if( ch=='\\' ) { dest[index++]=src[i+1];i++;}
	   else dest[index++]=ch;
   }
}
int fileinput=0;
//char * gdata="{\"took\":4,\"timed_out\":false,\"_shards\":{\"total\":30,\"successful\":30,\"skipped\":0,\"failed\":0},\"hits\":{\"total\":1210,\"max_score\":4.5573797,\"hits\":[{\"_index\":\"perfdata-2017-12\",\"_type\":\"check_system\",\"_id\":\"AWAYeiA-aDW_TpXSq3kY\",\"_score\":4.5573797,\"_source\":{ \"@timestamp\":\"2017-12-03T02:25:29+0800\",\"name\":\"1.3\",\"severity\":1,\"idc\":\"cp\",\"system.load\":6,\"system.mem\":55,\"system.cpu\":15,\"system.io\":0,\"system.wio\":0,\"system.\\/\":37 }}]}}";
int main(int argc,char * argv[])
{
      char * cp;
	  int i;
      if ((cp = strrchr(argv[0], CONST_PATH_SEP)) != NULL)
		strcpy(mProgram ,cp + 1 );
	else strcpy(mProgram ,argv[0]);
      if ((cp = strrchr(mProgram, '.')) != NULL)	cp[0]=0;
	  if( argc<2 ) {
		  Usage();
		  return -1;
	  }
	  if( strcmp(argv[1],"-h")==0 ) {
		  Usage();
		  return 0;
	  }
	  if( strcmp(argv[1],"--h")==0 ) {
		  Usage();
		  return 0;
	  }

	  for(i=0;i<16;i++) gNames[i] = NULL;

	  int optIndex =1;
	  if( strcmp(argv[1],"-f")==0 ) {
		  fileinput=1;
		  optIndex ++;
	  }

	  if( optIndex >= argc ) {
		  printf("%s: missing search object name.\n",mProgram);
		  Usage(); return -1; 
	  }
	  cp = strtok(argv[optIndex++],"/");
	  i =0;
	  while(cp){
		  gNames[i] = new char[strlen(cp)+4];
		  sprintf(gNames[i],"\"%s\"",cp);
		  i++;
		  cp = strtok(NULL,"/");
	  }
	  if( optIndex >= argc ) { 
		  printf("%s: missing input.\n",mProgram);
		  Usage(); return -1; 
	  }
	  TString input;
	  if( fileinput ){ 
		 // TString s;
		  GetFileString(argv[optIndex],input);
		  //StrCopy(s,input.GetBuffer(s.GetLength()));
		  //input.ReleaseBuffer();
	  } else {
		  for( i=2;i<argc;i++){
			  input.Append(' ');
			  input.Append(argv[i]);
		  }
	  }
	  if( input.GetLength()<2 ){
		  printf("%s: missing input.\n",mProgram);
		  Usage();
		  return -1;
	  }
	 // printf("Search node : %s\n",argv[1]);
	  //printf("Search Text: %s\n",(char *)input.Ptr());
	  SLexer lex(input);
	  JsonNode * head = ReadNode(lex);
//	  if( head ) head->print(stdout);
	  if( head) {
           JsonNode * t1 = FindChildFirst(head,0);
		   if( t1 ) t1->print(stdout,0);
		   printf("\n");
	  }
	  for( i=0;i<16;i++) if( gNames[i] ) delete gNames[i];
	  if( head ) delete head;
      return 0;
}
